#include "vas_algorithm.h"
#include "vas_rocketmq.h"

namespace vas {

    AlgorithmData* BuildAlgorithmData(const cv::Mat &image, const Channel &channel, long timestamp) {
        auto* data = new AlgorithmData();
        data->image = image;
        data->channel = channel;
        data->timestamp = timestamp;
        data->ctime = GetCurrentTime();
        data->mtime = data->ctime;
        data->uuid = channel.channel + "-" + std::to_string(data->ctime);
        std::set<std::string> task_types;
        for (const auto &t : channel.tasks) {
            if (t.type != FIRE_DETECTION_TASK && t.type != HUMAN_DETECTION_TASK) {
                std::stringstream msg;
                msg << "Unknown Task Type " << t.type << " is found in channel " << channel.channel;
                VAS_ERROR(msg.str());
                continue;
            }
            task_types.insert(t.type);
        }
        if (task_types.find(FIRE_DETECTION_TASK) != task_types.end()) {
            data->routes.emplace_back(FIRE_RECOGNITION_ALGO);
        }
        if (task_types.find(HUMAN_DETECTION_TASK) != task_types.end()) {
            data->routes.emplace_back(HUMAN_DETECTION_ALGO);
        }
        data->routes.emplace_back(SEND_MSG);
        return data;
    }

    void RouteAlgorithm(AlgorithmData *data, std::map<std::string, bool> algo_mock) {
        if (data->traces.size() >= data->routes.size()) {
            return;
        }
        std::string next = data->routes[data->traces.size()];
        std::stringstream msg;
        msg << "Route " << data->uuid << " to " << next << ", progress " << data->traces.size() + 1 << "/"
            << data->routes.size();
        VAS_INFO(msg.str());
        if (next == FIRE_RECOGNITION_ALGO) {
            FireRecognitionProcess(data, algo_mock[FIRE_RECOGNITION_ALGO]);
        } else if (next == HUMAN_DETECTION_ALGO) {
            HumanDetectionProcess(data, algo_mock[HUMAN_DETECTION_ALGO]);
        } else if (next == SEND_MSG) {
            SendMsg(data);
        } else {
            std::stringstream msg2;
            msg2 << "Unknown Algorithm " << next;
            VAS_ERROR(msg2.str());
            throw VasNotFound(msg2.str());
        }
        data->mtime = GetCurrentTime();
        data->traces.emplace_back(next);
    }

    std::string ConvertAlgorithmResult(AlgorithmData *data, const std::set<std::string> &task_types) {
        Json::Value res_data;
        res_data["channel"] = data->channel.channel;
        res_data["image"] = MatToBase64(data->image);
        res_data["timestamp"] = data->timestamp;
        Json::Value task_results;
        for (auto &task : task_types) {
            if (task == HUMAN_DETECTION_TASK) {
                if (data->pedestrians.empty()) {
                    continue;
                }
                Json::Value result;
                result["type"] = task;
                for (auto &p: data->pedestrians) {
                    Json::Value det;
                    det["label"] = "NG";
                    det["coordinate"].append(p.box.top_left.x);
                    det["coordinate"].append(p.box.top_left.y);
                    det["coordinate"].append(p.box.bottom_right.x);
                    det["coordinate"].append(p.box.bottom_right.y);
                    result["result"].append(det);
                }
                task_results.append(result);
            } else if (task == FIRE_DETECTION_TASK) {
                if (data->scenes.find(FIRE_LABELS[0]) == data->scenes.end()) {
                    continue;
                }
                Json::Value result;
                result["type"] = task;
                Json::Value det;
                det["label"] = "NG";
                det["coordinate"].append(0);
                det["coordinate"].append(0);
                det["coordinate"].append(data->image.cols);
                det["coordinate"].append(data->image.rows);
                result["result"].append(det);
                task_results.append(result);
            } else {
                VAS_ERROR("Unknown task type " + task);
            }
        }
        res_data["tasks"] = task_results;
        return res_data.toStyledString();
    }

    void SendMsg(AlgorithmData *data) {
        std::set<std::string> task_types;
        for (auto &i : data->channel.tasks) {
            try {
                long last_ts = *(long *) GetValue(i.type, data->channel.channel, MSG_SEND_LAST_TS);
                if (data->mtime - last_ts > i.interval) {
                    task_types.insert(i.type);
                    SaveValue(i.type, &data->mtime, 1, data->channel.channel, MSG_SEND_LAST_TS);
                }
            }
            catch (VasNotFound &e) {
                SaveValue(i.type, &data->mtime, 1, data->channel.channel, MSG_SEND_LAST_TS);
            }
        }

        if (task_types.empty()) {
            return;
        }

        if (ROCKET_MQ_HOST.empty()) {
            VAS_INFO("RocketMQ host is empty, skip sending message");
            return;
        }

        std::string msg_str = ConvertAlgorithmResult(data, task_types);

        RocketMQSend(msg_str);
        std::stringstream info;
        info << "Sent MSG " << data->channel.channel;
        VAS_INFO(info.str());
    }

    void FireRecognitionInit(bool mock) {
        if (mock) {
            std::stringstream msg;
            msg << FIRE_RECOGNITION_ALGO << " init with mock";
            VAS_INFO(msg.str());
            return;
        }
        //TODO 非MOCK初始化实现
    }

    void FireRecognitionProcess(AlgorithmData *data, bool mock) {
        if (mock) {
            std::stringstream msg;
            msg << FIRE_RECOGNITION_ALGO << " process with mock";
            VAS_INFO(msg.str());
            data->scenes.insert(FIRE_LABELS[RandomInt(0, 1)]);
            return;
        }
        //TODO 非MOCK处理实现
    }

    void HumanDetectionInit(bool mock) {
        if (mock) {
            std::stringstream msg;
            msg << HUMAN_DETECTION_ALGO << " init with mock";
            VAS_INFO(msg.str());
            return;
        }
        //TODO 非MOCK初始化实现
    }

    void HumanDetectionProcess(AlgorithmData *data, bool mock) {
        if (mock) {
            std::stringstream msg;
            msg << HUMAN_DETECTION_ALGO << " process with mock";
            VAS_INFO(msg.str());
            int det_size = RandomInt(0, 3);
            for (int i = 0; i < det_size; i++) {
                PedestrianData ped;
                Box box{};
                Coord tl{0, 0};
                Coord br{RandomInt(1, data->image.cols), RandomInt(1, data->image.rows)};
                box.top_left = tl;
                box.bottom_right = br;
                ped.box = box;
                data->pedestrians.emplace_back(ped);
            }
            return;
        }
        //TODO 非MOCK处理实现
    }
}
